/******************************************************************************
 * FileName:   i2c.c
 * Purpose:    Librer�a de funciones para el m�dulo TWI en modo I2C Master
 * Processor:  ATmel AVR con m�dulo TWI
 * Compiler:   AVR IAR C y AVR GCC (WinAVR)
 * Author:     Shawn Johnson. http://www.cursomicros.com.
 *
 * Copyright (C) 2008 - 2013 Shawn Johnson. All rights reserved.
 *
 * License:    Se permiten el uso y la redistribuci�n de este c�digo con 
 *             modificaciones o sin ellas, siempre que se mantengan esta 
 *             licencia y las notas de autor y copyright de arriba.
--
Modificado por Rodrigo Garcia, mayo 2015, <strysg@riseup.net>
 *****************************************************************************/

#include "i2c.h"

//****************************************************************************
// Inicializar el m�dulo TWI.
// Configurar Frecuencia de reloj a I2C_BAUD.
//****************************************************************************
void i2c_init(void)
{
 /* NOTA: Internal PullUPS */
  PORTC |= (1<<1) | (0<<1);
  
    /* Establecer valor de prescaler de TWI a 0  */
    TWSR &= ~((1<<TWPS1)|(1<<TWPS0));
    
    /* Establecer Frecuencia de SCL */
    TWBR = ((F_CPU/I2C_BAUD)-16)/2;
    
    /* Habilitar la interface TWI */
    TWCR = (1<<TWEN);
}

//****************************************************************************
// Env�a una Condici�n START o START Repetida.
// Retorna 1 si la Condici�n START se envi� satisfactoriamente, 0 si fall�.
//****************************************************************************
char i2c_start(void)
{
    /* Setear bit TWSTA y limpiar flag TWINT para iniciar Condici�n START */
    TWCR = (1<<TWSTA)|(1<<TWINT)|(1<<TWEN);
    
    /* Esperar a que la Condici�n START se termine de enviar */
    /* toma I2C_MAX_WAIT ciclos para que se envie START*/
    unsigned int max_cicles = I2C_MAX_WAIT;
    while( ((TWCR & (1<<TWINT))==0) && (max_cicles--));
    
    /* Limpiar bit TWSTA */
    TWCR = (1<<TWEN);   
    
    /* Comprobar si la transferencia de START fue satisfactoria */
    if( ((TWSR&0xFC) == 0x08) || ((TWSR&0xFC) == 0x10) )
        return 1;
    return 0;
}

//****************************************************************************
// Env�a una Condici�n STOP
//****************************************************************************
void i2c_stop(void)
{
    /* Setear bit TWSTO y limpiar flag TWINT para iniciar Condici�n STOP */
    TWCR = (1<<TWSTO)|(1<<TWINT)|(1<<TWEN);
    
    /* Esperar a que la Condici�n STOP se termine de enviar */
    // fixme: is it necesary to control this loop?
    while( TWCR & (1<<TWSTO) ); 
}

//****************************************************************************
// Env�a el byte 'data' y devuelve 0 si el esclavo respondi� con un bit ACK
// En otro caso, devuelve 1.
//****************************************************************************
char i2c_write(char data)
{
    /* Cargar dato a transmitir */
    TWDR = data;
    
    /* Limpiar flag TWINT para iniciar la transferencia */
    TWCR = (1<<TWINT)|(1<<TWEN);
    
    /* Esperar a que finalice la transferencia */
    /* hace hasta I2C_MAX_WAIT iteraciones para la transferencia */
    unsigned char max_cicles = I2C_MAX_WAIT;
    while( ((TWCR & (1<<TWINT))==0) && (max_cicles--) );
    
    /* Leer el resultado de la transferencia y retornar 0 si el esclavo
     * respondi� con un bit ACK.
     */
    if( ((TWSR&0xFC)==0x18)||((TWSR&0xFC)==0x40)||((TWSR&0xFC)==0x28) )
        return 0;
        
    /* Se llega a este punto si el esclavo respondi� con un NACK o si
     * hubo un error en la transferencia */
    return 1;
}

//****************************************************************************
// Lee un byte de dato y env�a el bit ACK/NACK.
// ack = 0 es ACK y ack = 1 es NACK.
//****************************************************************************
char i2c_read(char ack)
{
    /* Esperar a que termine de llegar el dato pedido */
    while( (TWCR & (1<<TWINT))==0 );
    
    /* Enviar el bit ACK o NACK al esclavo */
    if(ack == 1)
        TWCR = (1<<TWINT)|(1<<TWEN);              // Esto es NACK
    else
        TWCR = (1<<TWEA)|(1<<TWINT)|(1<<TWEN);    // Esto es ACK

    /* Esperar a que se termine de enviar el bit ACK/NACK */
    // FIXME: Is it necessary to control this loop and report error? if it takes long time?
    while( (TWCR & (1<<TWINT))==0 );
    
    /* Retornar el dato que llego anteriormente */
    return TWDR;
}

